package com.fengwk.support.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fengwk.support.exception.BaseException;
import com.fengwk.support.function.Ref;
import com.fengwk.support.function.Return;
import com.fengwk.support.function.ReturnBoolean;
import com.fengwk.support.function.Void;
import com.fengwk.support.util.LoggerUtils.Logger;

public class CollectionUtils {

	private static final Logger LOG = LoggerUtils.getLogger(CollectionUtils.class);
	
	private static final String DEFAULT_JOIN_SEPARATOR = ",";
	
	@SuppressWarnings("rawtypes")
	private static final Class<? extends List> DEFAULT_LIST_CLASS = ArrayList.class;
	@SuppressWarnings("rawtypes")
	private static final Class<? extends Set> DEFAULT_SET_CLASS = HashSet.class;
	
	private CollectionUtils() {
		throw new BaseException("can not instance " + getClass());
	}
	
	/**
	 * 拷贝一个新的Collection
	 * 
	 * @param collection
	 * @return
	 */
	@Deprecated
	public static <E> Collection<E> copy(Collection<E> collection) {
		@SuppressWarnings("unchecked")
		Collection<E> copyCollection = ObjectUtils.build(collection.getClass());
		copyCollection.addAll(collection);
		return copyCollection;
	}
	
	/**
	 * 弃用空集合
	 * 
	 * @param list
	 * @return
	 */
	public static <C extends Collection<E>, E> C deprecatedBlank(C collection) {
		if (CollectionUtils.isBlank(collection)) {
			return null;
		}
		return collection;
	}

	/**
	 * 集合为空判断
	 * 
	 * @param collection
	 * @return
	 */

	public static <E> boolean isBlank(Collection<E> collection) {
		if (collection == null || collection.size() <= 0) {
			return true;
		}
		return false;
	}

	/**
	 * 集合非空判断
	 * 
	 * @param collection
	 * @return
	 */
	public static <E> boolean isNotBlank(Collection<E> collection) {
		return !isBlank(collection);
	}

	/**
	 * 集合转数组
	 * 
	 * @param collection
	 * @param arrayClazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <E, T> E[] toArray(Collection<E> collection, Class<T> arrayClazz) {
		return collection.toArray((E[]) Array.newInstance(arrayClazz, collection.size()));
	}

	/**
	 * 集合合并
	 * 
	 * @param src 元数据集合
	 * @param tar 目标集合
	 * @return
	 */
	public static <E> void merge(Collection<E> src, Collection<E> tar) {
		if (isBlank(src) || isBlank(tar)) {
			return;
		}
		Iterator<E> it = src.iterator();
		while (it.hasNext()) {
			tar.add(it.next());
		}
	}
	
	/**
	 * 填充List
	 * 
	 * @param index 填充位置,从0开始
	 * @param list 填充目标集合
	 * @param fillObj 填充物
	 * @param fillModel 填充模型
	 */
	@SuppressWarnings({ "deprecation", "unchecked" })
	public static <T> void fillList(int index, @SuppressWarnings("rawtypes") List list, Object fillObj, Class<T> fillModel) {
		if(list == null) {
			return;
		}
		int len = list.size();
		if(len < index + 1) {
			for(int i = len; i < index; i ++) {
				T fillModelObj = null;
				if(fillModel != null) {
					fillModelObj = ObjectUtils.build(fillModel);
				}
				list.add(fillModelObj);
			}
			list.add(fillObj);
		}else {
			list.remove(index);
			list.add(index, fillObj);
		}
	}
	
	public static <T> String join(Collection<T> collection) {
		return join(collection, DEFAULT_JOIN_SEPARATOR);
	}
	
	/**
	 * String collection 组合
	 * 
	 * @param collection
	 * @param separator
	 * @return
	 */
	public static <E> String join(Collection<E> collection, String separator) {
		return join(collection, separator, e -> e);
	}
	
	public static <K, E> String join(Collection<E> collection, String separator, Return.Params1<K, E> eToK) {
		if(isBlank(collection)) {
			return null;
		}
		StringBuffer sb = new StringBuffer();
		int end = 0;
		for(E e: collection) {
			K k = eToK.apply(e);
			if(k != null) {
				sb.append(k).append(separator);
			}
			end ++;
		}
		if(end <= 0) {
			return null;
		}
		return sb.substring(0, sb.length() - separator.length());
	}
	
	/**
	 * 打印集合
	 * 
	 * @param collection
	 */
	public static <E> void print(Collection<E> collection) {
		if (isBlank(collection)) {
			return;
		}
		for (Object ele : collection) {
			System.out.println(ele);
		}
	}
	
	public static <S, T> List<T> toListWithTypeConvert(Collection<S> sCollection, Return.Params1<T, S> sToT, boolean excludeNull) {
		List<T> tList = new ArrayList<>();
		if(isNotBlank(sCollection)) {
			for(S s: sCollection) {
				if (excludeNull && s == null)
					continue;
				tList.add(sToT.apply(s));
			}
		}
		return tList;
	}
	
	public static <S, T> List<T> toListWithTypeConvert(Collection<S> srcCollection, Return.Params1<T, S> srcToTar) {
		return toListWithTypeConvert(srcCollection, srcToTar, false);
	}
	
	public static <S> List<S> toList(Collection<S> srcCollection, boolean excludeNull) {
		return toListWithTypeConvert(srcCollection, s -> s);
	}
	
	public static <S> List<S> toList(Collection<S> srcCollection) {
		return toList(srcCollection, false);
	}
	
	public static <S, T> Set<T> toSetWithTypeConvert(Collection<S> sCollection, Return.Params1<T, S> sToT, boolean excludeNull) {
		Set<T> tList = new HashSet<>();
		if(isNotBlank(sCollection)) {
			for(S s: sCollection) {
				if (excludeNull && s == null)
					continue;
				tList.add(sToT.apply(s));
			}
		}
		return tList;
	}
	
	public static <S, T> Set<T> toSetWithTypeConvert(Collection<S> sCollection, Return.Params1<T, S> sToT) {
		return toSetWithTypeConvert(sCollection, sToT, false);
	}
	
	public static <S> Set<S> toSet(Collection<S> sCollection, boolean excludeNull) {
		return toSetWithTypeConvert(sCollection, s -> s, excludeNull);
	}
	
	public static <S> Set<S> toSet(Collection<S> sCollection) {
		return toSet(sCollection, false);
	}
	
	public static <K, S, T> Map<K, T> toMapWithTypeConvert(Collection<S> sCollection, Return.Params1<K, S> getKey, Return.Params1<T, S> sToT, boolean excludeNull) {
		Map<K, T> map = new LinkedHashMap<>();
		if(isNotBlank(sCollection)) {
			for(S s: sCollection) {
				if (excludeNull && s == null)
					continue;
				K key = getKey.apply(s);
				map.put(key, sToT.apply(s));
			}
		}
		return map;
	}
	
	public static <K, S, T> Map<K, T> toMapWithTypeConvert(Collection<S> sCollection, Return.Params1<K, S> getKey, Return.Params1<T, S> sToT) {
		return toMapWithTypeConvert(sCollection, getKey, sToT, false);
	}
	
	public static <K, E> Map<K, E> toMap(Collection<E> oCollection, Return.Params1<K, E> getKey, boolean excludeNull) {
		return toMapWithTypeConvert(oCollection, getKey, e -> e, excludeNull);
	}
	
	public static <K, E> Map<K, E> toMap(Collection<E> oCollection, Return.Params1<K, E> getKey) {
		return toMapWithTypeConvert(oCollection, getKey, e -> e);
	}
	
	public static <K, S, T> Map<K, List<T>> toListMapWithTypeConvert(Collection<S> sCollection, Return.Params1<K, S> getKey, Return.Params1<T, S> sToT, boolean excludeNull) {
		Map<K, List<T>> map = new LinkedHashMap<>();
		if(isNotBlank(sCollection)) {
			for(S s: sCollection) {
				if (excludeNull && s == null)
					continue;
				K key = getKey.apply(s);
				List<T> tarListBuf = map.get(key);
				if(isBlank(tarListBuf)) {
					tarListBuf = new ArrayList<T>();
					map.put(key, tarListBuf);
				}
				tarListBuf.add(sToT.apply(s));
			}
		}
		return map;
	}
	
	public static <K, S, T> Map<K, List<T>> toListMapWithTypeConvert(Collection<S> sCollection, Return.Params1<K, S> getKey, Return.Params1<T, S> sToT) {
		return toListMapWithTypeConvert(sCollection, getKey, sToT, false);
	}
	
	public static <K, E> Map<K, List<E>> toListMap(Collection<E> oCollection, Return.Params1<K, E> getKey, boolean excludeNull) {
		return toListMapWithTypeConvert(oCollection, getKey, e -> e, excludeNull);
	}
	
	public static <K, E> Map<K, List<E>> toListMap(Collection<E> oCollection, Return.Params1<K, E> getKey) {
		return toListMapWithTypeConvert(oCollection, getKey, e -> e);
	}
	
	public static <K, S, T> Map<K, Set<T>> toSetMapWithTypeConvert(Collection<S> sCollection, Return.Params1<K, S> getKey, Return.Params1<T, S> srcToTar, boolean excludeNull) {
		Map<K, Set<T>> map = new LinkedHashMap<>();
		if(isNotBlank(sCollection)) {
			for(S s: sCollection) {
				if (excludeNull && s == null)
					continue;
				K key = getKey.apply(s);
				Set<T> tarSetBuf = map.get(key);
				if(isBlank(tarSetBuf)) {
					tarSetBuf = new LinkedHashSet<T>();
					map.put(key, tarSetBuf);
				}
				tarSetBuf.add(srcToTar.apply(s));
			}
		}
		return map;
	}
	
	public static <K, S, T> Map<K, Set<T>> toSetMapWithTypeConvert(Collection<S> sCollection, Return.Params1<K, S> getKey, Return.Params1<T, S> srcToTar) {
		return toSetMapWithTypeConvert(sCollection, getKey, srcToTar, false);
	}
	
	public static <K, E> Map<K, Set<E>> toSetMap(Collection<E> oCollection, Return.Params1<K, E> getKey, boolean excludeNull) {
		return toSetMapWithTypeConvert(oCollection, getKey, e -> e, excludeNull);
	}
	
	public static <K, E> Map<K, Set<E>> toSetMap(Collection<E> oCollection, Return.Params1<K, E> getKey) {
		return toSetMapWithTypeConvert(oCollection, getKey, e -> e);
	}
	
	public static <E> List<List<E>> splitCollectionByGroupSize(Collection<E> collection, int groupSize) {
		int temp = 0;
		List<List<E>> res = new ArrayList<>();
		if(isNotBlank(collection)) {
			List<E> buf = new ArrayList<>();
			for(E e: collection) {
				buf.add(e);
				if(++ temp >= groupSize) {
					res.add(buf);
					temp = 0;
					buf = new ArrayList<E>();
				}
			}
			if(isNotBlank(buf)) {
				res.add(buf);
			}
		}
		return res;
	}
	
	public static <E> void splitCollectionByGroupSizeWithProcess(Collection<E> collection, int groupSize, Void.Params1<List<E>> processList) {
		if (isNotBlank(collection)) {
			List<List<E>> groupList = splitCollectionByGroupSize(collection, groupSize);
			if (isNotBlank(groupList)) {
				for (List<E> group: groupList) {
					processList.apply(group);
				}
			}
		}
	}
	
	public static <E> boolean contains(Collection<E> collection, ReturnBoolean.Params1<E> ifFun) {
		if(isNotBlank(collection)) {
			for(E eBuf: collection) {
				if (ifFun.apply(eBuf))
					return true;
			}
		}
		return false;
	}
	
	public static <E> E getFirstFromList(List<E> list) {
		E e = null;
		if (isNotBlank(list)) {
			e = list.get(0);
		}
		return e;
	}
	
	public static <E> E getAnyFromSet(Set<E> set) {
		E e = null;
		if (isNotBlank(set)) {
			return set.iterator().next();
		}
		return e;
	}
	
	/**
	 * 动态创建集合对象
	 * 
	 * @param collectionClass
	 * @return
	 */
	public static <C extends Collection<E>, E> Collection<E> buildCollection(Class<C> collectionClass) {
		Collection<E> collection = null;
		try {
			collection = collectionClass.newInstance();
		} catch (InstantiationException | IllegalAccessException e) {
		    LOG.error(e);
		}
		return collection;
	}
	
	/**
	 * 构建集合
	 * 
	 * @param collectionClass
	 * @param es
	 * @param excludeNull
	 * @return
	 */
	public static <C extends Collection<E>, E> Collection<E> buildCollection(Class<C> collectionClass, E[] es, boolean excludeNull) {
		Collection<E> collection = null;
		if (ArrayUtils.isNotBlank(es)) {
			for (E e: es) {
				if (excludeNull && e == null)
					continue;
				if (collection == null)
					collection = buildCollection(collectionClass);
				collection.add(e);
			}
		}
		return collection;
	}
	
	/**
	 * 构建集合
	 * 
	 * @param collectionClass
	 * @param es
	 * @return
	 */
	public static <C extends Collection<E>, E> Collection<E> buildCollection(Class<C> collectionClass, E[] es) {
		return buildCollection(collectionClass, es, false);
	}
	
	/**
	 * 构建List
	 * 
	 * @param listClass
	 * @param es
	 * @param excludeNull
	 * @return
	 */
	public static <L extends List<E>, E> List<E> buildList(Class<L> listClass, E[] es, boolean excludeNull) {
		return (List<E>) buildCollection(listClass, es, excludeNull);
	}
	
	/**
	 * 构建List
	 * 
	 * @param listClass
	 * @param es
	 * @return
	 */
	public static <L extends List<E>, E> List<E> buildList(Class<L> listClass, E[] es) {
		return (List<E>) buildCollection(listClass, es);
	}
	
	/**
	 * 构建List
	 * 
	 * @param es
	 * @param excludeNull
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <E> List<E> buildList(E[] es, boolean excludeNull) {
		return buildList(DEFAULT_LIST_CLASS, es, excludeNull);
	}
	
	/**
	 * 构建List
	 * 
	 * @param es
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <E> List<E> buildList(E[] es) {
		return buildList(DEFAULT_LIST_CLASS, es);
	}
	
	/**
	 * 构建List
	 * 
	 * @param e
	 * @param excludeNull
	 * @return
	 */
	public static <E> List<E> buildList(E e, boolean excludeNull) {
		if (excludeNull && e == null)
			return null;
		@SuppressWarnings("unchecked")
		Collection<E> collection = buildCollection(DEFAULT_LIST_CLASS);
		collection.add(e);
		return (List<E>) collection;
	}
	
	/**
	 * 构建List
	 * 
	 * @param e
	 * @return
	 */
	public static <E> List<E> buildList(E e) {
		return buildList(e, false);
	}
	
	/**
	 * 构建Set
	 * 
	 * @param setClass
	 * @param es
	 * @param excludeNull
	 * @return
	 */
	public static <S extends Set<E>, E> Set<E> buildSet(Class<S> setClass, E[] es, boolean excludeNull) {
		return (Set<E>) buildCollection(setClass, es, excludeNull);
	}
	
	/**
	 * 构建Set
	 * 
	 * @param setClass
	 * @param es
	 * @return
	 */
	public static <S extends Set<E>, E> Set<E> buildSet(Class<S> setClass, E[] es) {
		return (Set<E>) buildCollection(setClass, es);
	}
	
	/**
	 * 构建Set
	 * 
	 * @param es
	 * @param excludeNull
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <E> Set<E> buildSet(E[] es, boolean excludeNull) {
		return buildSet(DEFAULT_SET_CLASS, es, excludeNull);
	}
	
	/**
	 * 构建Set
	 * 
	 * @param es
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <E> Set<E> buildSet(E[] es) {
		return buildSet(DEFAULT_SET_CLASS, es);
	}
	
	/**
	 * 构建Set
	 * 
	 * @param e
	 * @param excludeNull
	 * @return
	 */
	public static <E> Set<E> buildSet(E e, boolean excludeNull) {
		if (excludeNull && e == null)
			return null;
		@SuppressWarnings("unchecked")
		Collection<E> collection = buildCollection(DEFAULT_SET_CLASS);
		collection.add(e);
		return (Set<E>) collection;
	}
	
	/**
	 * 构建Set
	 * 
	 * @param e
	 * @return
	 */
	public static <E> Set<E> buildSet(E e) {
		return buildSet(e, false);
	}
	
	/**
	 * 获取交集
	 * @param collections
	 * @return
	 */
	@SafeVarargs
	public static <E> Set<E> intersection(Collection<E>...collections) {
		Set<E> set = null;
		if (ArrayUtils.isNotBlank(collections)) {
			for (Collection<E> c: collections) {
				if (set == null) {
					set = new HashSet<E>();
					set.addAll(c);
				}
				set.retainAll(c);
			}
		}
		return set;
	}
	
	/**
	 * 组合,将集合内元素无序结合,效率低,应当使用手写使用for循环形式
	 * 
	 * @param c 被组合的数组
	 * @param limit 组合个数
	 * @param proc 操作组合,传入为组合列表
	 */
	@Deprecated
	public static <E> void combination(Collection<E> c, int limit, Void.Params1<List<E>> proc) {
		if (c == null)
			throw new BaseException("c cannot be null");
		if (proc == null)
			throw new BaseException("proc cannot be null");
		if (c.size() < limit)
			throw new BaseException("c.size() < limit");
		combinationIterator(0, Ref.of(0), new ArrayList<>(), c, limit, proc);
	}
	
	// 组合迭代器
	private static <E> void combinationIterator(int level, Ref<Integer> i, List<E> eList, Collection<E> c, int limit, Void.Params1<List<E>> proc) {
		c.stream().parallel().skip(i.value).forEach(e -> {
			eList.add(e);
			if (level + 1 < limit) {
				combinationIterator(level + 1, Ref.of(i.value + 1), eList, c, limit, proc);
				i.value ++;
			} else {
				proc.apply(eList);
			}
			eList.remove(level);
		});
	}
	
}
